from odcutils._tools import cookbook as cbt
import re

def iscomment(line):
    """Determines if line of cld file is comment"""
    if line.strip()[0:2] == '//':
        return True


def parse_filename(lines):
    """Finds file name in cdl file"""
    for line in lines:
        if iscomment(line): continue
        if line.find('{') > -1:
            fname = line.strip().split('{')[0].split(' ')[-1]
        else:
            continue

        return fname

def parse_dims(lines):
  i=-1
  i0=-1
  i1=-1
  while i<len(lines)-1:
    i+=1
    if lines[i].lstrip().find('dimensions:')==0: i0=i+1
    elif (lines[i].lstrip().find('variables:')==0 or\
           lines[i].lstrip().find('group:')==0 or\
           lines[i].lstrip().find('} // group ')==0) and i0>-1:
      i1=i
      break
  res=cbt.odict()
  for j in range(i0,i1):
      tmp=lines[j].split('=')
      dimname=tmp[0].strip()
#      dimvalue=tmp[1][:-1].strip()
      dimvalue=tmp[1][:-1].strip(' \n ;')
      res[dimname]=dimvalue
  return typeconv(res)


def parse_vars_v3(lines):
    """New function to parse variables for netcdf v<4"""
    res=cbt.odict()
    # Find variables: lines starting with type declarations
    types = ['char', 'byte', 'short', 'int', 'float', 'double']
    # Line number limits for variables
    varln = []
    vars = []
    # Find out where all the variables are
    for line in lines:
    # Skip if comment
        if iscomment(line): continue
    # Get first word in line
        try:
            istype = line.split()[0].strip()
    # Catch if istype is empty
        except IndexError:
            continue
    # Find next type declaration
        try:
            if types.index(istype):
    # Append line number to list 
                varln.append(lines.index(line))
    # Get variable name and dependence
                name = re.findall('\w+', line)[1]
                vars.append(name)
                res[name] = {}
                res[name]['ivar'] = re.findall('\w+', line)[2]
                res[name]['type'] = istype
        except ValueError:
            continue
    # Read attributes of each variable
    try:
        for i, ln in enumerate(varln):
    # Set beginning and end of variable block +1 to skip type declaration
            i_b = ln + 1
            i_e = varln[i + 1]
    # Bulk load the block
            res[vars[i]]['raw'] = lines[i_b: i_e]
            res[vars[i]]['atts'] = cbt.odict()
    # Parse the block
            for line in res[vars[i]]['raw']:
    # Skip if comment
                if iscomment(line):
                    continue
                else:
                    vatt = re.findall('\w+', line)[1]
    # Assign the attributes to the res dict
                    res[vars[i]]['atts'][vatt] = {} 
                    res[vars[i]]['atts'][vatt] = \
                            line.split('=')[-1].strip('\n ; ')
    # Delete the raw key
            del res[vars[i]]['raw']
    # Type convert all attributes of variable
            res[vars[i]]['atts'] = typeconv(res[vars[i]]['atts'])
    # Read last block and exit when cycle ends
    except IndexError:
    # The last variable block is read by scanning the file from the
    # variable delclaration until the EOF since there is no way of 
    # defining the end of the block
    #
    # Seach string:
        res[vars[i]]['atts'] = cbt.odict()
        for line in lines[varln[-1] :-1]:
            lnstrp = line.strip()
            if lnstrp.split(':')[0] == vars[i]:
                [atn, at] = lnstrp.split(':')[-1].strip().split('=')
                vatt = atn.strip()
                res[vars[i]]['atts'][vatt] = at.strip('\n ; ')
        res[vars[i]]['atts'] = typeconv(res[vars[i]]['atts'])
    return res


def parse_atts_v3(lines):
    """Parses global attributes regardless of order in nc file."""
    res = cbt.odict()
    for line in lines:
        try:
            if line.strip()[0] == ':':
    # Get attribute name
                att = line.strip().split('=')[0].split(':')[1].strip()
                res[att] = {}
    # Get attribute value
                res[att] = line.split('=')[1].strip(' \n ;')
    # Catch if line is only whitespace
        except IndexError:
            continue
    return typeconv(res)


def typeconv(resdict):
    """Convert the result dictionaries from all string into strings and
    floats where appropriate"""
    for k in resdict:
    # find if there are " in the string if not treat it as number
        if resdict[k] == resdict[k].strip('"'):
            resdict[k] = map(float, re.split('[,;]', resdict[k]))
    # if single value then convert list to float if not convert to tuple
            if len(resdict[k]) == 1:
                resdict[k] = resdict[k][0]
            else:
                tuple(resdict[k])
        else:
    # if it is in fact a string, strip the "
            resdict[k] = resdict[k].strip('"')
    return resdict


def ncgen(cdl_name, nc_name = (), aformat = 'NETCDF3_CLASSIC'):
    """Create netCDF file from cdl script.
    This function uses the netCDF4 for python module.
    Format can be 'NETCDF3_CLASSIC' (default), 'NETCDF3_64BIT', 
    'NETCDF4_CLASSIC' and  'NETCDF4'"""
    from netCDF4 import Dataset
    # Open cdl file
    f = open(cdl_name, 'r')
    # Read cdl file
    lines = f.readlines()
    # Close cdl file
    f.close()
    # Parse cdl file
    if not nc_name:
        nc_name = parse_filename(lines) + '.nc'
    dims = parse_dims(lines)
    vars = parse_vars_v3(lines)
    atts = parse_atts_v3(lines)
    # Open netcdf file
    nc = Dataset(nc_name, "w", format = aformat)
    # Create dimensions
    for dim in dims.keys():
        nc.createDimension(dim, dims[dim])
    # Create variables
    for var in vars.keys():
        nc.createVariable(var, vars[var]['type'], vars[var]['ivar'])
    # Create attributes of each variable
        for vatt in vars[var]['atts'].keys():
            nc.variables[var].setncattr(vatt , vars[var]['atts'][vatt])
    # Create global attributes
    for att in atts.keys():
        nc.setncattr(att, atts[att])
    # Close netcdf file
    nc.close()
    return [dims, vars, atts]
